#ifndef _core_bitstream_archive_hpp_
#define _core_bitstream_archive_hpp_

/** \file basic_bitstream.hpp Contains the bitstream_iarchive and bitstream_oarchive classes, which are thin wrappers around
 *
 *  \author Adrian Schweizer
 *  \created  $Sa 18 Aug 03:48:01 pm CEST 2007 schwadri@SchwadriComp.local$
 *  \modified $Fr 31 Aug 03:05:08 pm CEST 2007 schwadri@SchwadriComp.local$
 *
 *  core::bitstream that implement the boost::archive concept.
 *  \warning Tracking and object generation is not supported atm.
 *  \par FIXME
 *  - string size validation
 *
 */

#include <exception>

#include <boost/archive/detail/iserializer.hpp>
#include <boost/archive/detail/common_iarchive.hpp>
#include <boost/archive/detail/oserializer.hpp>
#include <boost/archive/detail/common_oarchive.hpp>
#include "bitstream.hpp"

//namespace boost { namespace archive {
namespace core {

    /** \brief exception thrown by bitstream_iarchive
     *
     *  May be thrown during deserialization if the underlying bitstream fails to read a variable
     *  because there was not enough data in the buffer
     */
    class bitstream_iarchive_exception : public std::exception
    { };

    /** \brief an archive that reads from a bitstream (for use with boost::serialization)
     *
     *  \note no type or tracking information is available in the archive. Thus the object you want to deserialize
     *  has to be constructed before and only its data members may be loaded from the archive
     */
    class bitstream_iarchive : public  boost::archive::detail::common_iarchive<bitstream_iarchive>
    {
    public:
        /** \brief ctor
         *
         *  \param[in] in       a reference to the bitstream from which the data has to be read
         *  \param[in] flags    \a optional. defaults to 0. additional archive flags.
         *                      check the documentation of boost::serialization for information.
         */
        bitstream_iarchive(core::bitstream& in, unsigned int flags=0)
        :   boost::archive::detail::common_iarchive<bitstream_iarchive>(flags),
            m_in(in)
        { }

        /** \name Archive Interface*///@{
        void load_binary(void *address, std::size_t size)
        {
            if(!m_in.read(reinterpret_cast<char*>(address),size))
                throw   bitstream_iarchive_exception();
        }

        template<class T>
            void load_override(T & t, unsigned int)
            {
                boost::archive::load(* this->This(), t);
            }

        //override
        void load_override(boost::archive::version_type & t, int)
        { }

        void load_override(boost::archive::object_id_type & t, int)
        { }

        void load_override(boost::archive::object_reference_type & t, int)
        { }

        void load_override(boost::archive::class_id_type & t, int)
        { }

        void load_override(boost::archive::class_id_optional_type & t, int)
        { }

        void load_override(boost::archive::class_id_reference_type & t, int)
        { }

        void load_override(boost::archive::tracking_type & t, int)
        { }

        void load_override(boost::archive::class_name_type & t, int)
        { }
        //@}
      private:
        friend class boost::archive::load_access;
        template<class T>
            void load(T & t)
            {
                if(!m_in.read(t))
                    throw   bitstream_iarchive_exception();
            }

        /** \internal
         *  \brief load specialization for std::string
         */
        void load(std::string& s)
        {
            std::size_t l;
            this->This()->load(l);
            //TODO: do proper length check in order to avoid possible stack smashing attacks
            //      but to do this properly the loading object needs to be able to specify
            //      the maximum allowed length, thus breaking boost::archive compatibility
            // borland de-allocator fixup
            #if BOOST_WORKAROUND(_RWSTD_VER, BOOST_TESTED_AT(20101))
            if(NULL != s.data())
            #endif
                s.resize(l);
            //note breaking a rule here - could be a problem on some platforms
            load_binary(const_cast<char *>(s.data()), l);
        }
        core::bitstream&     m_in;
    };

    /** \brief an archive that writes to a bitstream (for use with boost::serialization)
     *
     *  \note No object tracking or type information is saved (only its data members are serialized).
     */
    class bitstream_oarchive    :   public  boost::archive::detail::common_oarchive<bitstream_oarchive>
    {
    public:
        /** \brief ctor
         *
         *  \param[in] out      a reference to the bitstream to which the archive will write
         *  \param[in] flags    \a optional. defaults to 0. additional archive flags.
         *                      check the documentation of boost::serialization for information.
         */
        bitstream_oarchive(core::bitstream& out,unsigned int flags=0)
        :   boost::archive::detail::common_oarchive<bitstream_oarchive>(flags),
            m_out(out)
        { }

        /** \name Archive Interface*///@{
        void save_binary(const void *address, std::size_t size)
        {
            m_out.write(reinterpret_cast<const char*>(address),size);
        }

        template<class T>
            void save_override(const T & t, unsigned int v)
            {
                boost::archive::save(* this->This(), t);
            }

        void save_override(const boost::archive::version_type & t, unsigned int)
        { }

        void save_override(const boost::archive::object_id_type & t, unsigned int)
        { }

        void save_override(const boost::archive::object_reference_type & t, unsigned int)
        { }

        void save_override(const boost::archive::class_id_type & t, unsigned int)
        { }

        void save_override(const boost::archive::class_id_optional_type & t, unsigned int)
        { }

        void save_override(const boost::archive::class_id_reference_type & t, unsigned int)
        { }

        void save_override(const boost::archive::tracking_type & t, unsigned int)
        { }

        void save_override(const boost::archive::class_name_type & t, unsigned int)
        { }
        //@}

    private:
        friend class boost::archive::save_access;
        template<class T>
            void save(const T & t)
            {
                m_out.write(t);
            }

        /** \internal
         *  \brief save specialization for std::string
         */
        void save(const std::string& s)
        {
            std::size_t l = static_cast<unsigned int>(s.size());
            save(l);
            save_binary(s.data(), l);
        }

        core::bitstream&     m_out;
    };
} // namespace core
/*} // namespace archive
} // namespace boost
*/

/*namespace core {
    //import archive into core
    using boost::archive::bitstream_iarchive_exception;
    using boost::archive::bitstream_iarchive;
    using boost::archive::bitstream_oarchive;
} // namespace core

*/
#endif // _core_bitstream_archive_hpp_
